package org.jgs1904.elasticsearchapi.doc;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.jgs1904.elasticsearchapi.doc.entity.User;
import org.junit.Test;

import java.io.IOException;

/**
 * @author 墨苍璃
 * @date 2023年06月06日00时04分
 * <p>
 * doc 高级查询
 */
public class QueryDoc {

    /**
     * 全量查询
     */
    @Test
    public void queryAllDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 全量查询
        SearchResponse<User> searchResponse = client.search(e -> e.index("user_test").query(q -> q.matchAll(m -> m)), User.class);
        HitsMetadata<User> hits = searchResponse.hits();
        for (Hit<User> hit : hits.hits()) {
            assert hit.source() != null;
            System.out.println("user = " + hit.source());
        }
        assert searchResponse.hits().total() != null;
        System.out.println("searchResponse.hits().total().value() = " + searchResponse.hits().total().value());

        transport.close();
        restClient.close();
    }


    /**
     * 条件查询
     *
     * @throws IOException
     */
    @Test
    public void conditionQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 条件查询
        SearchResponse<User> searchResponse = client.search(
                s -> s.index("user_test").query(q -> q.match(m -> m.field("name").query("test3"))), User.class);
        searchResponse.hits().hits().forEach(h -> {
            assert h.source() != null;
            System.out.println(h.source());
        });

        transport.close();
        restClient.close();
    }

//    /**
//     * 条件查询
//     *
//     * @throws IOException
//     */
//    @Test
//    public void conditionQueryDocument1() throws IOException {
//        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
//        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
//        ElasticsearchClient client = new ElasticsearchClient(transport);
//
//        // 条件查询
//        String searchText = "liuyihu";
//        SearchResponse<User> response = client.search(s -> s
//                        // 我们要搜索的索引的名称
//                        .index("users")
//                        // 搜索请求的查询部分（搜索请求也可以有其他组件，如聚合）
//                        .query(q -> q
//                                // 在众多可用的查询变体中选择一个。我们在这里选择匹配查询（全文搜索）
//                                .match(t -> t
//                                        // name配置匹配查询：我们在字段中搜索一个词
//                                        .field("name")
//                                        .query(searchText)
//                                )
//                        ),
//                // 匹配文档的目标类
//                User.class
//        );
//        TotalHits total = response.hits().total();
//        boolean isExactResult = total.relation() == TotalHitsRelation.Eq;
//
//        Logger log = null;
//        if (isExactResult) {
//            log.info("There are " + total.value() + " results");
//        } else {
//            log.info("There are more than " + total.value() + " results");
//        }
//
//        List<Hit<User>> hits = response.hits().hits();
//        for (Hit<User> hit : hits) {
//            User user = hit.source();
//            assert user != null;
//            log.info("Found userId " + user.getId() + ", name " + user.getName());
//        }
//
//    }

    /**
     * 分页查询
     *
     * @throws IOException
     */
    @Test
    public void pagingQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 分页查询
        SearchResponse<User> searchResponse = client.search(
                s -> s.index("user_test")
                        .query(q -> q.matchAll(m -> m))
                        .from(1)//从0开始
                        .size(1), User.class);

        searchResponse.hits().hits().forEach(h -> {
            assert h.source() != null;
            System.out.println(h.source());
        });

        transport.close();
        restClient.close();
    }


    /**
     * 查询排序
     *
     * @throws IOException
     */
    @Test
    public void sortQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 排序查询
        SearchResponse<User> searchResponse = client.search(
                s -> s.index("user_test")
                        .query(q -> q.matchAll(m -> m))
                        .sort(o -> o.field(f -> f.field("age").order(SortOrder.Asc))), User.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString()));

        transport.close();
        restClient.close();
    }


    /**
     * 组合查询
     * <p>
     * must
     *
     * @throws IOException
     */
    @Test
    public void combinationQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 组合查询
        SearchResponse<User> searchResponse = client.search(
                s -> s.index("user_test").query(q -> q.bool(b -> b
                        .must(m -> m.match(u -> u.field("age").query(20)))
                        .must(m -> m.match(u -> u.field("sex").query("male")))
                        .mustNot(m -> m.match(u -> u.field("sex").query("female")))
                ))
                , User.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source()));

        transport.close();
        restClient.close();

    }


    /**
     * 组合查询
     * <p>
     * should
     *
     * @throws IOException
     */
    @Test
    public void combinationQueryDocument2() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 组合查询
        SearchResponse<User> searchResponse = client.search(
                s -> s.index("user_test").query(q -> q.bool(b -> b
                        .should(h -> h.match(u -> u.field("age").query(19)))
                        .should(h -> h.match(u -> u.field("sex").query("female")))
                )), User.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source()));

        transport.close();
        restClient.close();
    }


    /**
     * 范围查询
     *
     * @throws IOException
     */
    @Test
    public void scopeQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 范围查询，gte()表示取大于等于，gt()表示大于，lte()表示小于等于
        SearchResponse<User> searchResponse = client.search(s -> s.index("user_test").query(q -> q
                .range(r -> r.field("age").gte(JsonData.of(20)).lt(JsonData.of(21)))), User.class);

        searchResponse.hits().hits().forEach(h -> System.out.println(h.source()));

        transport.close();
        restClient.close();
    }


    /**
     * 模糊查询
     * fuzziness:
     * Levenshtein Edit Distance 叫做莱文斯坦距离**，是编辑距离的一种。
     * 指两个字串之间，由一个转成另一个所需的最少编辑操作次数。允许的编辑操作包括将一个字符替换成另一个字符，插入一个字符，删除一个字符。
     *
     * @throws IOException
     */
    @Test
    public void fuzzyQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 模糊查询，fuzziness表示差几个可以查询出来
        SearchResponse<User> searchResponse = client.search(s -> s.index("user_test").query(q -> q
                .fuzzy(f -> f.field("name").value("test").fuzziness("2"))), User.class);

        searchResponse.hits().hits().forEach(h -> System.out.println(h.source()));

        transport.close();
        restClient.close();
    }


    /**
     * 高亮查询
     *
     * @throws IOException
     */
    @Test
    public void highlightQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 高亮查询
        SearchResponse<User> searchResponse = client.search(s -> s.index("user_test").query(q -> q
                .term(t -> t.field("name").value("test3")))
                .highlight(h -> h.fields("name", f -> f.preTags("<font color='red'>").postTags("</font>"))), User.class);
        searchResponse.hits().hits().forEach(h -> System.out.println(h.source()));

        transport.close();
        restClient.close();
    }


    /**
     * 聚合查询
     * 最大值查询
     *
     * @throws IOException
     */
    @Test
    public void aggregateQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 聚合查询，取最大年龄
        SearchResponse<User> searchResponse = client.search(s -> s.index("user_test").aggregations("maxAge", a -> a.max(m -> m.field("age"))), User.class);
        // searchResponse.aggregations().entrySet().forEach(f -> System.out.println(f.getKey() + ":" + f.getValue().max().value()));
        searchResponse.aggregations().forEach((key, value) -> System.out.println(key + ":" + value.max().value()));

        transport.close();
        restClient.close();
    }


    /**
     * 分组查询
     *
     * @throws IOException
     */
    @Test
    public void groupQueryDocument() throws IOException {
        RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient client = new ElasticsearchClient(transport);

        // 分组查询
        SearchResponse<User> searchResponse = client.search(s -> s.index("user_test")
                .aggregations("ageGroup", a -> a.terms(t -> t.field("age"))), User.class);

        searchResponse.aggregations().get("ageGroup").lterms().buckets().array().forEach(f -> System.out.println(f.key() + ":" + f.docCount()));

        transport.close();
        restClient.close();
    }


}
